Analysis of Cyber-security Threats

The purpose of this data analysis is to help model and predict cyber-security threats to help optimize global digital security. As well, to analyze which defense mechanisms that are optimal in combating cyber attacks. The dataset contains 10 variables that are potential significant covariates in predicting potential threats. The variable country refers to the country where the attack occurred. The other variables analyze incident resolution time in hours, number of affected users, financial loss, attack type, target industry, attack source, security vulnerability type and defense mechanism used. Here is a full list of variables:

  • Country country where the attack occurred
  • Year year of the incident
  • Attack Type type of cyber-security threat
  • Target Industry industry targeted
  • Financial Loss estimated financial loss in millions
  • Number of Affected Users number of users impacted by the attack
  • Attack Source origin of the attack
  • Security Vulnerability Type type of vulnerability exploited
  • Defense Mechanism Used cyber-security defense strategy applied
  • Incident Resolution Time time taken to fully resolve event in hours
cyber <- read.csv('https://raw.githubusercontent.com/GabbyK8/Datasets/refs/heads/main/Global_Cybersecurity_Threats_2015-2024.csv')
cyber$Defense.Mechanism.Used <- ifelse(cyber$Defense.Mechanism.Used == "AI-based Detection", "AI", cyber$Defense.Mechanism.Used)

# plot 1 number of affected users by financial loss by vulnerability type


ggplot(cyber, aes(x = cyber$Number.of.Affected.Users, y = cyber$Financial.Loss..in.Million..., color = cyber$Security.Vulnerability.Type)) +
geom_smooth(aes(x = cyber$Number.of.Affected.Users, y = cyber$Financial.Loss..in.Million..., color = cyber$Security.Vulnerability.Type), method = "lm", se = FALSE) +
  scale_size(range = c(2, 10)) +
  labs(
    title = "Plot of Security Vulnerability, Volume Compromised, and Financial Impact",
    x = "Volume Compromised",
    y = "Financial Impact",
    color = "Security Vulernability"
  ) +
  theme_minimal()
This plot shows the relationship between security vulnerability type and the interaction between the number of affected users and financial loss in millions. In most cases the number of affected users seems to be positively correlated with financial loss. The only exception is seen in zero-day. zero-day refers to a type of vulnerability where the target of the attack is unaware of the attack. Meaning there are zero days for the problem to be solved before it has been exploited. It would make sense that in this type of vulnerability, the fewer users affected would lead to a greater financial loss.

This plot shows the relationship between security vulnerability type and the interaction between the number of affected users and financial loss in millions. In most cases the number of affected users seems to be positively correlated with financial loss. The only exception is seen in zero-day. zero-day refers to a type of vulnerability where the target of the attack is unaware of the attack. Meaning there are zero days for the problem to be solved before it has been exploited. It would make sense that in this type of vulnerability, the fewer users affected would lead to a greater financial loss.

# plot 3 Density plot of attacked industries
Year_data <- cyber %>%
  filter(Year == 2015)
p1<- ggplot(Year_data, aes(x = Number.of.Affected.Users, fill = Country)) +
  geom_density(alpha = 0.6) +
  labs(
    x = "Number of Affected Users",
    y = "Density",
    fill="Country",
    title = "Density Plot of Attacked Countries in 2015"
  ) +
  theme_minimal()
ggplotly(p1)

This plot shows the density of number of affected users in cybersecurity attacks in each country in the year 2015.

 Year_data1 <- cyber %>%
  filter(Year == 2024)
# plot 4 Density plot of Attacked countries
p2 <- ggplot(Year_data1, aes(x = Number.of.Affected.Users, fill = Country)) +
  geom_density(alpha = 0.6) +
  labs(
    x = "Number of Affected Users",
    y = "Density",
    fill="Country",
    title = "Density Plot of Attacked Countries in 2024"
  ) +
  theme_minimal()
ggplotly(p2)

This plot shows the density of number of affected users in cybersecurity attacks in each country in the year 2024.

Each country is given a specific color and the number of affected users is displayed on the x-axis. The density is displayed on the y-axis. Density in this plot shows the relative frequency of number of affected users per attack in each country.In 2015, the cyber attacks within the USA more frequently affected about 310,000 users, compared to Russia where attacks more frequently attacked 670,000 users and Germany where most attacks more frequently affected 790,000 users. In 2024, the number of affected users in the USA does not seem to be much more dense in any one number and is more dispersed. Japan had more frequent attacks that affected 500,000 and Brazil had it’s most frequent at 750,000 users.

ggplot(cyber, aes(x = cyber$Year, y = cyber$Incident.Resolution.Time..in.Hours., color = cyber$Security.Vulnerability.Type)) +
geom_smooth(aes(x = cyber$Year, y = cyber$Incident.Resolution.Time..in.Hours., color = cyber$Security.Vulnerability.Type), method = "lm", se = FALSE) +
  scale_size(range = c(2, 10)) +
  labs(
    title = "Plot of Security Vulnerability, Year of Attack, and Resolution Time",
    x = "Year of Attack",
    y = "Incident Resolution Time in Hours",
    color = "Security Vulernability"
  ) +
  theme_minimal()
This plot shows the relationship between security vulnerability type and the interaction between the year of attack and resolution time. The plot shows how over the years resolution time has improved for certain vulnerability types, such as weak passwords, unpatched software, and social engineering. However, for zero-day there is an increase in resolution time, showing a positive relationship between zero-day and resolution time.

This plot shows the relationship between security vulnerability type and the interaction between the year of attack and resolution time. The plot shows how over the years resolution time has improved for certain vulnerability types, such as weak passwords, unpatched software, and social engineering. However, for zero-day there is an increase in resolution time, showing a positive relationship between zero-day and resolution time.

#plot of zero-day attack type by target industry
zero_data <- cyber %>%
  filter(Security.Vulnerability.Type == "Zero-day") 
a <- ggplot(zero_data, aes(x = zero_data$Attack.Type, fill =zero_data$Target.Industry )) +
  geom_bar(position = "dodge") +
  labs(
    x = "Threat Type",
    y = "Proportion",
    fill = "Target Industry",
    title = "Zero-day Incidences by Threat Type vs. Target Industry"
  ) +
  theme_minimal()
ggplotly(a)

This plot focuses on zero-day vulnerability and shows the proportion of threat types in each industry. The x-axis shows each threat type and the y-axis shows the proportion of attacks within the dataset that fall into each category. The different colors are meant to show a subcategory, target industry. This shows the proportion of the dataset that falls within each category of threat type in each target industry.

Zero-day attacks seem to commonly use Phising within both retail and IT industries, based on the height and count of the bar in these two categories. Ransomware is the most minimally used zero-day attack on the government, with a count of just 12.

col0=c("#4682B4", "#B4464B","#9900FF","#FF66FF", "#009933")
boxplot(Incident.Resolution.Time..in.Hours. ~ Defense.Mechanism.Used,
        data=cyber,
        xlab="Defense Mechanism",
        ylab="Resolution Time (hours)",
        col = col0,
        main="Incident Resolution Time by Defense Mechanism",
        cex.main = 0.9,
        col.main = "blue")
The bloxplot shows the relationship between defense mechanism and resolution time in hours.

The bloxplot shows the relationship between defense mechanism and resolution time in hours.

The boxplot shows that the average resolution time among attacks based on defense mechanisms is relatively the same. The only exception seems to be firewall, which appears to take slightly less resolution time on average.We can indicate the average by looking the black horizontal line within each box. They all appear to line up with one another indicating similar averages.

Skewness

cyber$Country[cyber$Country=="USA"]<-1
cyber$Country<- ifelse(cyber$Country!="USA",0)
cyber$Security.Vulnerability.Type[cyber$Security.Vulnerability.Type=="Zero-day"]<-1
cyber$Security.Vulnerability.Type<-ifelse(cyber$Security.Vulnerability.Type!="Zero-day",0)
cyber$Number.of.Affected.Users<-as.numeric(cyber$Number.of.Affected.Users)
cyber$Year<-as.numeric(as.factor(cyber$Year))
cyber$Incident.Resolution.Time..in.Hours.<-as.numeric(cyber$Incident.Resolution.Time..in.Hours.)


c<-skewness(cyber$Financial.Loss..in.Million...)
d<-skewness(cyber$Number.of.Affected.Users)
e<-skewness(cyber$Year) 

Skewness = cbind(Financial.Loss = c, 
                 Affected.Users =  d, 
                 Year = e)

pander(Skewness)
Financial.Loss Affected.Users Year
-0.01684 -0.02537 -0.02749

The data is normally skewed and no transformation is necessary.

Linear Regression

Linear regression is explored below:

# Predictors (x) and Response Variable (y)

set.seed(12345)
X <- as.data.frame(cyber[, -5])  

y <- cyber$Financial.Loss..in.Million...


train_index <- createDataPartition(y, p = 0.8, list = FALSE)
X_train <- makeX(X[train_index, ])
X_test <- makeX(X[-train_index, ])
y_train <- y[train_index]
y_test <- y[-train_index]

#standardizing the data
preprocess_params <- preProcess(X_train, method = c("center","scale"))
X_train <- predict(preprocess_params, X_train)
X_test <- predict(preprocess_params, X_test)


#fitting the model
fit_lasso <- glmnet(X_train, y_train, alpha = 1)  # Lasso
fit_ridge <- glmnet(X_train, y_train, alpha = 0)  # Ridge
fit_elastic_net <- glmnet(X_train, y_train, alpha = 0.5)  # Elastic Net
#cross-validation
cv_lasso <- cv.glmnet(X_train, y_train, alpha = 1)       # Lasso CV
cv_ridge <- cv.glmnet(X_train, y_train, alpha = 0)       # Ridge CV
cv_elastic_net <- cv.glmnet(X_train, y_train, alpha = 0.5)  # Elastic Net CV

#plot coefficient path
par(mar=c(5,4,6,3))
# Plot coefficient path
plot(fit_lasso, xvar = "lambda", label = TRUE,
     lwd = 1.5,
     main = "Coefficient Path Analysis: LASSO",
     cex.main = 0.9,
     col = rainbow(10))
abline(v = log(1), col = "purple", lty = 4, lwd = 2)
abline(v = -1, col = "steelblue", lty = 2, lwd = 2)
The x-axis of the coefficient path analysis shows the log of lambda. A low lambda has less regularization and is closer to ordinary least squares. A high lambda indicates higher regularization and at this lambda many coefficients will shrink to zero. The Y-axis shows the coefficient values for each variable predictor for financial loss. The lines shows the path of each coefficient has lambda differs. The dashed line shows the optimal level of shrinkage. Coefficients to the right of this line are more forced towards zero. At this chosen lambda only certain variables will be non-zero and are deemed significant.

The x-axis of the coefficient path analysis shows the log of lambda. A low lambda has less regularization and is closer to ordinary least squares. A high lambda indicates higher regularization and at this lambda many coefficients will shrink to zero. The Y-axis shows the coefficient values for each variable predictor for financial loss. The lines shows the path of each coefficient has lambda differs. The dashed line shows the optimal level of shrinkage. Coefficients to the right of this line are more forced towards zero. At this chosen lambda only certain variables will be non-zero and are deemed significant.

pander(data.frame(colnames(X_train)[c(27, 5, 20, 12)]),caption= "Feature Variables at log(Lambda) = 0")
Feature Variables at log(Lambda) = 0
colnames.X_train..c.27..5..20..12..
Incident.Resolution.Time..in.Hours.
Attack.TypeMan-in-the-Middle
Attack.SourceUnknown
Target.IndustryHealthcare
pander(data.frame(colnames(X_train)[c(12, 20, 5, 26, 14, 1, 35, 9, 39, 36, 4, 3, 21, 19, 2,6)]),caption= " Feature Variables at log(Lambda) = -1")
Feature Variables at log(Lambda) = -1
colnames.X_train..c.12..20..5..26..14..1..35..9..39..36..4..3..
Target.IndustryHealthcare
Attack.SourceUnknown
Attack.TypeMan-in-the-Middle
Defense.Mechanism.UsedVPN
Target.IndustryRetail
Country
NA
Target.IndustryBanking
NA
NA
Attack.TypeMalware
Attack.TypeDDoS
Security.Vulnerability.Type
Attack.SourceNation-state
Year
Attack.TypePhishing
par(mar=c(5,4,6,3))
##
plot(cv_lasso, main = "RMSE Plot: LASSO",
     cex.main = 0.9)
The plot of performance shows that as lambda increases, the MSE decreases. The two vertical lines give a reference of lambda.

The plot of performance shows that as lambda increases, the MSE decreases. The two vertical lines give a reference of lambda.

# Extract coefficients for the best lambda
best.lasso.lambda <- cv_lasso$lambda.min
best.ridge.lambda <- cv_ridge$lambda.min
best.elastic.net.lambda <- cv_elastic_net$lambda.min
##
# Lasso Regression (L1 Regularization): 
# CAUTION: model formula differs from the regular regression formula 
lasso_model.opt <- glmnet(X_train, 
                      y_train, 
                      alpha = 1,      # lasso regression 
                      lambda = best.lasso.lambda,standardize = TRUE)   # useser selected alpha, optimal lambda
                                    # can be obtained through CV (see below)
lasso_predictions.opt <- predict(lasso_model.opt, 
                             s = best.lasso.lambda, # user selected lambda value 
                                      # (regularization paremeter)
                             newx = X_test)  # test data set 
# The following RMSE of prediction serves as a validation - one step validation
lasso_rmse.opt <- sqrt(mean((y_test - lasso_predictions.opt)^2))   
                                          
# Ridge Regression (L2 Regularization)
ridge_model.opt <- glmnet(X_train, y_train, alpha = 0, lambda = best.ridge.lambda,standardize=TRUE)
ridge_predictions.opt <- predict(ridge_model.opt, s = best.ridge.lambda, newx = X_test)
ridge_rmse.opt <- sqrt(mean((y_test - ridge_predictions.opt)^2))

# Elastic Net (Combination of L1 and L2)
elastic_net_model.opt <- glmnet(X_train, y_train, alpha = 0.5, lambda = 0.1,standardize = TRUE)
elastic_net_predictions.opt <- predict(elastic_net_model.opt, s = 0.1, newx = X_test)
elastic_net_rmse.opt <- sqrt(mean((y_test - elastic_net_predictions.opt)^2))

RMSE.opt = cbind(LASSO.opt = lasso_rmse.opt, 
                 Ridge.opt =  ridge_rmse.opt, 
                 Elasticnet.opt = elastic_net_rmse.opt)

pander(RMSE.opt)
LASSO.opt Ridge.opt Elasticnet.opt
28.79 28.72 28.89

The optimal lambda for each type of linear regression is shown above.

LASSO Regression Equation

Now, a final model will be extracted using LASSO, Ridge, and Elastic Net.

#Lasso
# Extract coefficients with names from glmnet
coef_lasso <- coef(cv_lasso, s = best.lasso.lambda)
coef_df <- as.data.frame(as.matrix(coef_lasso))
coef_df$feature <- rownames(coef_df)
colnames(coef_df)[1] <- "coefficient"

# Filter non-zero coefficients
nonzero_coefs <- subset(coef_df, coefficient != 0)

# Separate intercept
intercept <- round(nonzero_coefs$coefficient[nonzero_coefs$feature == "(Intercept)"], 4)
nonzero_terms <- subset(nonzero_coefs, feature != "(Intercept)")

# Build model equation string
equation <- paste0(round(nonzero_terms$coefficient, 4), "*", nonzero_terms$feature)
cat("Model equation: y =", intercept, "+", paste(equation, collapse = " + "), "\n")
Model equation: y = 50.5369 + 0.6683*Attack.TypeDDoS + -0.4171*Target.IndustryEducation + 0.6626*Target.IndustryGovernment + -0.3464*Target.IndustryHealthcare + 0.265*Attack.SourceHacker Group + -0.5335*Attack.SourceInsider + 0.0065*Defense.Mechanism.UsedAntivirus 

Ridge Regression Equation

# Extract coefficients with names from glmnet
coef_ridge <- coef(cv_ridge, s = best.ridge.lambda)
coef_df <- as.data.frame(as.matrix(coef_ridge))
coef_df$feature <- rownames(coef_df)
colnames(coef_df)[1] <- "coefficient"

# Filter non-zero coefficients
nonzero_coefs <- subset(coef_df, coefficient != 0)

# Separate intercept
intercept <- round(nonzero_coefs$coefficient[nonzero_coefs$feature == "(Intercept)"], 4)
nonzero_terms <- subset(nonzero_coefs, feature != "(Intercept)")

# Build model equation string
equation <- paste0(round(nonzero_terms$coefficient, 4), "*", nonzero_terms$feature)
cat("Model equation: y =", intercept, "+", paste(equation, collapse = " + "), "\n")
Model equation: y = 50.5369 + 0.0114*Year + 0.0521*Attack.TypeDDoS + -0.0125*Attack.TypeMalware + 0.0145*Attack.TypeMan-in-the-Middle + -0.0171*Attack.TypePhishing + -0.0201*Attack.TypeRansomware + -0.0186*Attack.TypeSQL Injection + 7e-04*Target.IndustryBanking + -0.0445*Target.IndustryEducation + 0.0576*Target.IndustryGovernment + -0.0398*Target.IndustryHealthcare + 0.0036*Target.IndustryIT + 0.0055*Target.IndustryRetail + 0.0187*Target.IndustryTelecommunications + 6e-04*Number.of.Affected.Users + 0.0427*Attack.SourceHacker Group + -0.0496*Attack.SourceInsider + 0.0182*Attack.SourceNation-state + -0.0106*Attack.SourceUnknown + -0.0045*Defense.Mechanism.UsedAI + 0.0233*Defense.Mechanism.UsedAntivirus + -0.0193*Defense.Mechanism.UsedEncryption + 0.0027*Defense.Mechanism.UsedFirewall + -0.0029*Defense.Mechanism.UsedVPN + -0.0122*Incident.Resolution.Time..in.Hours. 

Elastic Net Regression Equation

# Extract coefficients with names from glmnet
coef_elastic <- coef(cv_elastic_net, s = best.elastic.net.lambda)
coef_df <- as.data.frame(as.matrix(coef_elastic))
coef_df$feature <- rownames(coef_df)
colnames(coef_df)[1] <- "coefficient"

# Filter non-zero coefficients
nonzero_coefs <- subset(coef_df, coefficient != 0)

# Separate intercept
intercept <- round(nonzero_coefs$coefficient[nonzero_coefs$feature == "(Intercept)"], 4)
nonzero_terms <- subset(nonzero_coefs, feature != "(Intercept)")

# Build model equation string
equation <- paste0(round(nonzero_terms$coefficient, 4), "*", nonzero_terms$feature)
cat("Model equation: y =", intercept, "+", paste(equation, collapse = " + "), "\n")
Model equation: y = 50.5369 + 0.4941*Attack.TypeDDoS + -0.2438*Target.IndustryEducation + 0.5433*Target.IndustryGovernment + -0.168*Target.IndustryHealthcare + 0.1465*Attack.SourceHacker Group + -0.3984*Attack.SourceInsider 

Comparing Linear Regression with SVR

cyber <- read.csv('https://raw.githubusercontent.com/GabbyK8/Datasets/refs/heads/main/Global_Cybersecurity_Threats_2015-2024.csv')

#One-Hot Encoding
dummy <- dummyVars("~.", data=cyber)
cyber2<-data.frame(predict(dummy, newdata=cyber))

#####
# Split data into features (X) and target (y)
X <- cyber2[, -25]  # All columns except the target variable
y <- cyber2[, 25]   # Target variable (Financial Loss in Millions)

# create dummy variables



#####
# Split data into training and testing sets
set.seed(123)
train.index <- sample(1:nrow(cyber2), 0.8 * nrow(cyber2))
X.train <- X[train.index, ]
y.train <- y[train.index]
X.test <- X[-train.index, ]
y.test <- y[-train.index]

#####
cyber2.train <- as.data.frame(cyber2[train.index, ])
cyber2.test <- cyber2[-train.index, ]
#####
## Set up custom cross-validation control
my_tune_control <- tune.control(
  cross = 5,  # Use 5-fold cross-validation, the default is 10-fold cross-validation
  nrepeat = 1 # Number of repetitions (for repeated cross-validation)
)
#####
# Perform grid search for hyperparameter tuning: RBF kernel is used
tune.RBF <- tune(svm,
                 train.x = X.train, 
                 train.y = y.train, 
                    ranges = list(epsilon = seq(0.1, 0.5, 0.1), 
                                  cost = c(1, 10, 100), 
                                  gamma = c(0.01, 0.1, 1)), # Hyperpar in RBF
                    tunecontrol = my_tune_control    # 5-fold cross-validation

                                    )

####
# Display the best parameters
#print(tune.results$best.parameters)
#####
# Train the final model using the best parameters
final.RBF<- svm(X.train, y.train, 
                   type = "eps-regression",  # Use "nu-regression" for nu-SVR
                   kernel = "radial", 
                   epsilon = tune.RBF$best.parameters$epsilon, 
                   cost = tune.RBF$best.parameters$cost, 
                   gamma = tune.RBF$best.parameters$gamma)
#####
# Make predictions on the test set
pred.RBF <- predict(final.RBF, X.test)

# Evaluate performance
mse.RBF <- mean((y.test - pred.RBF)^2)    # mean square error
mae.RBF <- mean(abs(y.test - pred.RBF))   # mean absolute error

#### linear kernel

# Perform grid search for hyperparameter tuning: RBF kernel is used
tune.lin <- tune(svm, train.x = X.train, train.y = y.train, 
                    ranges = list(epsilon = seq(0.1, 0.5, 0.1), 
                                  cost = c(1, 10, 100)), 
                    tunecontrol = my_tune_control # 5-fold cross-validation
                    )
####
# Display the best parameters
#print(tune.lin$best.parameters)
#####
# Train the final model using the best parameters
final.lin<- svm(X.train, y.train, 
                   type = "eps-regression",  # Use "nu-regression" for nu-SVR
                   kernel = "linear", 
                   epsilon = tune.lin$best.parameters$epsilon, 
                   cost = tune.lin$best.parameters$cost)
#####
# Make predictions on the test set
pred.lin <- predict(final.lin, X.test)

# Evaluate performance
mse.lin <- mean((y.test - pred.lin)^2)    # mean square error
mae.lin <- mean(abs(y.test - pred.lin))

# After tuning is complete
## ordinary LSE regression model with stepwise variable selection
lse.fit <- lm(Financial.Loss..in.Million...~.,data=cyber2.train)
AIC.fit <- stepAIC(lse.fit,direction="both", trace = FALSE)
pred.lse <- predict(AIC.fit, X.test)
mse.lse <- mean((y.test - pred.lse)^2)    # mean square error
mae.lse <- mean(abs(y.test - pred.lse))   # mean absolute error
###
par(mfrow=c(2,2), mar=c(2,2,2,2))
plot(AIC.fit)
The plots are a diagnostic plot for the regression model.

The plots are a diagnostic plot for the regression model.

###
Performance <- data.frame(RBF.SVR=c(mse.RBF, mae.RBF),
                          Linear.SVR = c(mse.lin, mae.lin),
                          LSE.Reg =c(mse.lse, mae.lse))
row.names(Performance) <- c("MSE", "MAE")
##
pander(Performance)
  RBF.SVR Linear.SVR LSE.Reg
MSE 811.8 829.2 810.4
MAE 24.54 24.75 24.45

Based on our MSE and MAE scores, our least squares estimation has the best model. It has lower MSE and MAE scores indicating a better fit model.

LS0tCnRpdGxlOiAiUHJvamVjdCAyIFBhcnQgMSIKYXV0aG9yOiAiR2FicmllbGxhIEtoYWxpbCIKZGF0ZTogIjIwMjUtMDMtMjYiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OiAKICAgIHRvYzogeWVzCiAgICB0b2NfZGVwdGg6IDQKICAgIHRvY19mbG9hdDogeWVzCiAgICBudW1iZXJfc2VjdGlvbnM6IG5vCiAgICB0b2NfY29sbGFwc2VkOiB5ZXMKICAgIGNvZGVfZm9sZGluZzogaGlkZQogICAgY29kZV9kb3dubG9hZDogeWVzCiAgICBzbW9vdGhfc2Nyb2xsOiB5ZXMKICAgIHRoZW1lOiBsdW1lbgogIHdvcmRfZG9jdW1lbnQ6IAogICAgdG9jOiB5ZXMKICAgIHRvY19kZXB0aDogNAogICAgZmlnX2NhcHRpb246IHllcwogICAga2VlcF9tZDogeWVzCiAgcGRmX2RvY3VtZW50OiAKICAgIHRvYzogeWVzCiAgICB0b2NfZGVwdGg6IDQKICAgIGZpZ19jYXB0aW9uOiB5ZXMKICAgIG51bWJlcl9zZWN0aW9uczogbm8KICAgIGZpZ193aWR0aDogMwogICAgZmlnX2hlaWdodDogMwplZGl0b3Jfb3B0aW9uczogCiAgY2h1bmtfb3V0cHV0X3R5cGU6IGlubGluZQotLS0KCmBgYHs9aHRtbH0KCjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+CgovKiBDYXNjYWRpbmcgU3R5bGUgU2hlZXRzIChDU1MpIGlzIGEgc3R5bGVzaGVldCBsYW5ndWFnZSB1c2VkIHRvIGRlc2NyaWJlIHRoZSBwcmVzZW50YXRpb24gb2YgYSBkb2N1bWVudCB3cml0dGVuIGluIEhUTUwgb3IgWE1MLiBpdCBpcyBhIHNpbXBsZSBtZWNoYW5pc20gZm9yIGFkZGluZyBzdHlsZSAoZS5nLiwgZm9udHMsIGNvbG9ycywgc3BhY2luZykgdG8gV2ViIGRvY3VtZW50cy4gKi8KCmgxLnRpdGxlIHsgIC8qIFRpdGxlIC0gZm9udCBzcGVjaWZpY2F0aW9ucyBvZiB0aGUgcmVwb3J0IHRpdGxlICovCiAgZm9udC1zaXplOiAyMnB4OwogIGZvbnQtd2VpZ2h0OiBib2xkOwogIGNvbG9yOiBEYXJrUmVkOwogIHRleHQtYWxpZ246IGNlbnRlcjsKICBmb250LWZhbWlseTogIkdpbGwgU2FucyIsIHNhbnMtc2VyaWY7Cn0KaDQuYXV0aG9yIHsgLyogSGVhZGVyIDQgLSBmb250IHNwZWNpZmljYXRpb25zIGZvciBhdXRob3JzICAqLwogIGZvbnQtc2l6ZTogMThweDsKICBmb250LXdlaWdodDogYm9sZDsKICBmb250LWZhbWlseTogc3lzdGVtLXVpOwogIGNvbG9yOiBuYXZ5OwogIHRleHQtYWxpZ246IGNlbnRlcjsKfQpoNC5kYXRlIHsgLyogSGVhZGVyIDQgLSBmb250IHNwZWNpZmljYXRpb25zIGZvciB0aGUgZGF0ZSAgKi8KICBmb250LXNpemU6IDE4cHg7CiAgZm9udC1mYW1pbHk6IHN5c3RlbS11aTsKICBjb2xvcjogRGFya0JsdWU7CiAgdGV4dC1hbGlnbjogY2VudGVyOwogIGZvbnQtd2VpZ2h0OiBib2xkOwp9CmgxIHsgLyogSGVhZGVyIDEgLSBmb250IHNwZWNpZmljYXRpb25zIGZvciBsZXZlbCAxIHNlY3Rpb24gdGl0bGUgICovCiAgICBmb250LXNpemU6IDIycHg7CiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsKICAgIGNvbG9yOiBuYXZ5OwogICAgdGV4dC1hbGlnbjogY2VudGVyOwogICAgZm9udC13ZWlnaHQ6IGJvbGQ7Cn0KaDIgeyAvKiBIZWFkZXIgMiAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgZm9yIGxldmVsIDIgc2VjdGlvbiB0aXRsZSAqLwogICAgZm9udC1zaXplOiAyMHB4OwogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7CiAgICBjb2xvcjogbmF2eTsKICAgIHRleHQtYWxpZ246IGxlZnQ7CiAgICBmb250LXdlaWdodDogYm9sZDsKfQoKaDMgeyAvKiBIZWFkZXIgMyAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgb2YgbGV2ZWwgMyBzZWN0aW9uIHRpdGxlICAqLwogICAgZm9udC1zaXplOiAxOHB4OwogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7CiAgICBjb2xvcjogbmF2eTsKICAgIHRleHQtYWxpZ246IGxlZnQ7Cn0KCmg0IHsgLyogSGVhZGVyIDQgLSBmb250IHNwZWNpZmljYXRpb25zIG9mIGxldmVsIDQgc2VjdGlvbiB0aXRsZSAgKi8KICAgIGZvbnQtc2l6ZTogMThweDsKICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOwogICAgY29sb3I6IGRhcmtyZWQ7CiAgICB0ZXh0LWFsaWduOiBsZWZ0Owp9Cgpib2R5IHsgYmFja2dyb3VuZC1jb2xvcjp3aGl0ZTsgfQoKLmhpZ2hsaWdodG1lIHsgYmFja2dyb3VuZC1jb2xvcjp5ZWxsb3c7IH0KCnAgeyBiYWNrZ3JvdW5kLWNvbG9yOndoaXRlOyB9Cgo8L3N0eWxlPgpgYGAKCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQojIGNvZGUgY2h1bmsgc3BlY2lmaWVzIHdoZXRoZXIgdGhlIFIgY29kZSwgd2FybmluZ3MsIGFuZCBvdXRwdXQgCiMgd2lsbCBiZSBpbmNsdWRlZCBpbiB0aGUgb3V0cHV0IGZpbGVzLgppZiAoIXJlcXVpcmUoImtuaXRyIikpIHsKICAgaW5zdGFsbC5wYWNrYWdlcygia25pdHIiKQogICBsaWJyYXJ5KGtuaXRyKQp9CmlmICghcmVxdWlyZSgidGlkeXZlcnNlIikpIHsKICAgaW5zdGFsbC5wYWNrYWdlcygidGlkeXZlcnNlIikKbGlicmFyeSh0aWR5dmVyc2UpCn0KaWYgKCFyZXF1aXJlKCJHR2FsbHkiKSkgewogICBpbnN0YWxsLnBhY2thZ2VzKCJHR2FsbHkiKQpsaWJyYXJ5KEdHYWxseSkKfQoKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KHBsb3RseSkKbGlicmFyeShjb3dwbG90KQpsaWJyYXJ5KHBhbmRlcikKbGlicmFyeShtb21lbnRzKQpsaWJyYXJ5KGNhcmV0KQpsaWJyYXJ5KGdsbW5ldCkKIyBHZW5lcmFsCmxpYnJhcnkoZTEwNzEpICAgICAgICAjIEZvciBTVk0gICAgICAgIyBGb3IgUmlkZ2UsIExhc3NvLCBsb2dpc3RpYyByZWdyZXNzaW9uCmxpYnJhcnkocFJPQykgICAgICAgICAjIEZvciBST0MgY3VydmVzCmxpYnJhcnkoTWV0cmljcykgICAgICAjIEZvciBNU0UvTUFFCmxpYnJhcnkoTUFTUykKIyBPcHRpb25hbCB2aXN1YWxpemF0aW9uCmxpYnJhcnkoZ3JpZEV4dHJhKQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsICAgIyBpbmNsdWRlIGNvZGUgY2h1bmsgaW4gdGhlIG91dHB1dCBmaWxlCiAgICAgICAgICAgICAgICAgICAgICB3YXJuaW5nID0gRkFMU0UsIyBzb21ldGltZXMsIHlvdSBjb2RlIG1heSBwcm9kdWNlIHdhcm5pbmcgbWVzc2FnZXMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyB5b3UgY2FuIGNob29zZSB0byBpbmNsdWRlIHRoZSB3YXJuaW5nIG1lc3NhZ2VzIGluCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyB0aGUgb3V0cHV0IGZpbGUuIAogICAgICAgICAgICAgICAgICAgICAgcmVzdWx0cyA9IFRSVUUsICMgeW91IGNhbiBhbHNvIGRlY2lkZSB3aGV0aGVyIHRvIGluY2x1ZGUgdGhlIG91dHB1dAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgaW4gdGhlIG91dHB1dCBmaWxlLgogICAgICAgICAgICAgICAgICAgICAgbWVzc2FnZSA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgY29tbWVudCA9IE5BCiAgICAgICAgICAgICAgICAgICAgICApICAKCmBgYAoKIyBBbmFseXNpcyBvZiBDeWJlci1zZWN1cml0eSBUaHJlYXRzCgpUaGUgcHVycG9zZSBvZiB0aGlzIGRhdGEgYW5hbHlzaXMgaXMgdG8gaGVscCBtb2RlbCBhbmQgcHJlZGljdCBjeWJlci1zZWN1cml0eSB0aHJlYXRzIHRvIGhlbHAgb3B0aW1pemUgZ2xvYmFsIGRpZ2l0YWwgc2VjdXJpdHkuIEFzIHdlbGwsIHRvIGFuYWx5emUgd2hpY2ggZGVmZW5zZSBtZWNoYW5pc21zIHRoYXQgYXJlIG9wdGltYWwgaW4gY29tYmF0aW5nIGN5YmVyIGF0dGFja3MuIFRoZSBkYXRhc2V0IGNvbnRhaW5zIDEwIHZhcmlhYmxlcyB0aGF0IGFyZSBwb3RlbnRpYWwgc2lnbmlmaWNhbnQgY292YXJpYXRlcyBpbiBwcmVkaWN0aW5nIHBvdGVudGlhbCB0aHJlYXRzLiBUaGUgdmFyaWFibGUgY291bnRyeSByZWZlcnMgdG8gdGhlIGNvdW50cnkgd2hlcmUgdGhlIGF0dGFjayBvY2N1cnJlZC4gVGhlIG90aGVyIHZhcmlhYmxlcyBhbmFseXplIGluY2lkZW50IHJlc29sdXRpb24gdGltZSBpbiBob3VycywgbnVtYmVyIG9mIGFmZmVjdGVkIHVzZXJzLCBmaW5hbmNpYWwgbG9zcywgYXR0YWNrIHR5cGUsIHRhcmdldCBpbmR1c3RyeSwgYXR0YWNrIHNvdXJjZSwgc2VjdXJpdHkgdnVsbmVyYWJpbGl0eSB0eXBlIGFuZCBkZWZlbnNlIG1lY2hhbmlzbSB1c2VkLiBIZXJlIGlzIGEgZnVsbCBsaXN0IG9mIHZhcmlhYmxlczoKCi0gICAqKkNvdW50cnkqKiBjb3VudHJ5IHdoZXJlIHRoZSBhdHRhY2sgb2NjdXJyZWQKLSAgICoqWWVhcioqIHllYXIgb2YgdGhlIGluY2lkZW50Ci0gICAqKkF0dGFjayBUeXBlKiogdHlwZSBvZiBjeWJlci1zZWN1cml0eSB0aHJlYXQKLSAgICoqVGFyZ2V0IEluZHVzdHJ5KiogaW5kdXN0cnkgdGFyZ2V0ZWQKLSAgICoqRmluYW5jaWFsIExvc3MqKiBlc3RpbWF0ZWQgZmluYW5jaWFsIGxvc3MgaW4gbWlsbGlvbnMKLSAgICoqTnVtYmVyIG9mIEFmZmVjdGVkIFVzZXJzKiogbnVtYmVyIG9mIHVzZXJzIGltcGFjdGVkIGJ5IHRoZSBhdHRhY2sKLSAgICoqQXR0YWNrIFNvdXJjZSoqIG9yaWdpbiBvZiB0aGUgYXR0YWNrCi0gICAqKlNlY3VyaXR5IFZ1bG5lcmFiaWxpdHkgVHlwZSoqIHR5cGUgb2YgdnVsbmVyYWJpbGl0eSBleHBsb2l0ZWQKLSAgICoqRGVmZW5zZSBNZWNoYW5pc20gVXNlZCoqIGN5YmVyLXNlY3VyaXR5IGRlZmVuc2Ugc3RyYXRlZ3kgYXBwbGllZAotICAgKipJbmNpZGVudCBSZXNvbHV0aW9uIFRpbWUqKiB0aW1lIHRha2VuIHRvIGZ1bGx5IHJlc29sdmUgZXZlbnQgaW4gaG91cnMKCmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJywgZmlnLndpZHRoPTcsIGZpZy5oZWlnaHQ9NSwgZmlnLmNhcD0nVGhpcyBwbG90IHNob3dzIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBzZWN1cml0eSB2dWxuZXJhYmlsaXR5IHR5cGUgYW5kIHRoZSBpbnRlcmFjdGlvbiBiZXR3ZWVuIHRoZSBudW1iZXIgb2YgYWZmZWN0ZWQgdXNlcnMgYW5kIGZpbmFuY2lhbCBsb3NzIGluIG1pbGxpb25zLiBJbiBtb3N0IGNhc2VzIHRoZSBudW1iZXIgb2YgYWZmZWN0ZWQgdXNlcnMgc2VlbXMgdG8gYmUgcG9zaXRpdmVseSBjb3JyZWxhdGVkIHdpdGggZmluYW5jaWFsIGxvc3MuIFRoZSBvbmx5IGV4Y2VwdGlvbiBpcyBzZWVuIGluIHplcm8tZGF5LiB6ZXJvLWRheSByZWZlcnMgdG8gYSB0eXBlIG9mIHZ1bG5lcmFiaWxpdHkgd2hlcmUgdGhlIHRhcmdldCBvZiB0aGUgYXR0YWNrIGlzIHVuYXdhcmUgb2YgdGhlIGF0dGFjay4gTWVhbmluZyB0aGVyZSBhcmUgemVybyBkYXlzIGZvciB0aGUgcHJvYmxlbSB0byBiZSBzb2x2ZWQgYmVmb3JlIGl0IGhhcyBiZWVuIGV4cGxvaXRlZC4gSXQgd291bGQgbWFrZSBzZW5zZSB0aGF0IGluIHRoaXMgdHlwZSBvZiB2dWxuZXJhYmlsaXR5LCB0aGUgZmV3ZXIgdXNlcnMgYWZmZWN0ZWQgd291bGQgbGVhZCB0byBhIGdyZWF0ZXIgZmluYW5jaWFsIGxvc3MuJ30KY3liZXIgPC0gcmVhZC5jc3YoJ2h0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9HYWJieUs4L0RhdGFzZXRzL3JlZnMvaGVhZHMvbWFpbi9HbG9iYWxfQ3liZXJzZWN1cml0eV9UaHJlYXRzXzIwMTUtMjAyNC5jc3YnKQpjeWJlciREZWZlbnNlLk1lY2hhbmlzbS5Vc2VkIDwtIGlmZWxzZShjeWJlciREZWZlbnNlLk1lY2hhbmlzbS5Vc2VkID09ICJBSS1iYXNlZCBEZXRlY3Rpb24iLCAiQUkiLCBjeWJlciREZWZlbnNlLk1lY2hhbmlzbS5Vc2VkKQoKIyBwbG90IDEgbnVtYmVyIG9mIGFmZmVjdGVkIHVzZXJzIGJ5IGZpbmFuY2lhbCBsb3NzIGJ5IHZ1bG5lcmFiaWxpdHkgdHlwZQoKCmdncGxvdChjeWJlciwgYWVzKHggPSBjeWJlciROdW1iZXIub2YuQWZmZWN0ZWQuVXNlcnMsIHkgPSBjeWJlciRGaW5hbmNpYWwuTG9zcy4uaW4uTWlsbGlvbi4uLiwgY29sb3IgPSBjeWJlciRTZWN1cml0eS5WdWxuZXJhYmlsaXR5LlR5cGUpKSArCmdlb21fc21vb3RoKGFlcyh4ID0gY3liZXIkTnVtYmVyLm9mLkFmZmVjdGVkLlVzZXJzLCB5ID0gY3liZXIkRmluYW5jaWFsLkxvc3MuLmluLk1pbGxpb24uLi4sIGNvbG9yID0gY3liZXIkU2VjdXJpdHkuVnVsbmVyYWJpbGl0eS5UeXBlKSwgbWV0aG9kID0gImxtIiwgc2UgPSBGQUxTRSkgKwogIHNjYWxlX3NpemUocmFuZ2UgPSBjKDIsIDEwKSkgKwogIGxhYnMoCiAgICB0aXRsZSA9ICJQbG90IG9mIFNlY3VyaXR5IFZ1bG5lcmFiaWxpdHksIFZvbHVtZSBDb21wcm9taXNlZCwgYW5kIEZpbmFuY2lhbCBJbXBhY3QiLAogICAgeCA9ICJWb2x1bWUgQ29tcHJvbWlzZWQiLAogICAgeSA9ICJGaW5hbmNpYWwgSW1wYWN0IiwKICAgIGNvbG9yID0gIlNlY3VyaXR5IFZ1bGVybmFiaWxpdHkiCiAgKSArCiAgdGhlbWVfbWluaW1hbCgpCmBgYAoKYGBge3IgZmlnLmFsaWduPSdjZW50ZXInLCBmaWcud2lkdGg9NywgZmlnLmhlaWdodD01LCBmaWcuY2FwPSdUaGlzIHBsb3Qgc2hvd3MgdGhlIGRlbnNpdHkgb2YgbnVtYmVyIG9mIGFmZmVjdGVkIHVzZXJzIGluIGN5YmVyc2VjdXJpdHkgYXR0YWNrcyBpbiBlYWNoIGNvdW50cnkgaW4gdGhlIHllYXIgMjAxNS4gJ30KIyBwbG90IDMgRGVuc2l0eSBwbG90IG9mIGF0dGFja2VkIGluZHVzdHJpZXMKWWVhcl9kYXRhIDwtIGN5YmVyICU+JQogIGZpbHRlcihZZWFyID09IDIwMTUpCnAxPC0gZ2dwbG90KFllYXJfZGF0YSwgYWVzKHggPSBOdW1iZXIub2YuQWZmZWN0ZWQuVXNlcnMsIGZpbGwgPSBDb3VudHJ5KSkgKwogIGdlb21fZGVuc2l0eShhbHBoYSA9IDAuNikgKwogIGxhYnMoCiAgICB4ID0gIk51bWJlciBvZiBBZmZlY3RlZCBVc2VycyIsCiAgICB5ID0gIkRlbnNpdHkiLAogICAgZmlsbD0iQ291bnRyeSIsCiAgICB0aXRsZSA9ICJEZW5zaXR5IFBsb3Qgb2YgQXR0YWNrZWQgQ291bnRyaWVzIGluIDIwMTUiCiAgKSArCiAgdGhlbWVfbWluaW1hbCgpCmdncGxvdGx5KHAxKQpgYGAKCmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJywgZmlnLndpZHRoPTcsIGZpZy5oZWlnaHQ9NSwgZmlnLmNhcD0iVGhpcyBwbG90IHNob3dzIHRoZSBkZW5zaXR5IG9mIG51bWJlciBvZiBhZmZlY3RlZCB1c2VycyBpbiBjeWJlcnNlY3VyaXR5IGF0dGFja3MgaW4gZWFjaCBjb3VudHJ5IGluIHRoZSB5ZWFyIDIwMjQuICJ9CiBZZWFyX2RhdGExIDwtIGN5YmVyICU+JQogIGZpbHRlcihZZWFyID09IDIwMjQpCiMgcGxvdCA0IERlbnNpdHkgcGxvdCBvZiBBdHRhY2tlZCBjb3VudHJpZXMKcDIgPC0gZ2dwbG90KFllYXJfZGF0YTEsIGFlcyh4ID0gTnVtYmVyLm9mLkFmZmVjdGVkLlVzZXJzLCBmaWxsID0gQ291bnRyeSkpICsKICBnZW9tX2RlbnNpdHkoYWxwaGEgPSAwLjYpICsKICBsYWJzKAogICAgeCA9ICJOdW1iZXIgb2YgQWZmZWN0ZWQgVXNlcnMiLAogICAgeSA9ICJEZW5zaXR5IiwKICAgIGZpbGw9IkNvdW50cnkiLAogICAgdGl0bGUgPSAiRGVuc2l0eSBQbG90IG9mIEF0dGFja2VkIENvdW50cmllcyBpbiAyMDI0IgogICkgKwogIHRoZW1lX21pbmltYWwoKQpnZ3Bsb3RseShwMikKCmBgYAoKRWFjaCBjb3VudHJ5IGlzIGdpdmVuIGEgc3BlY2lmaWMgY29sb3IgYW5kIHRoZSBudW1iZXIgb2YgYWZmZWN0ZWQgdXNlcnMgaXMgZGlzcGxheWVkIG9uIHRoZSB4LWF4aXMuIFRoZSBkZW5zaXR5IGlzIGRpc3BsYXllZCBvbiB0aGUgeS1heGlzLiBEZW5zaXR5IGluIHRoaXMgcGxvdCBzaG93cyB0aGUgcmVsYXRpdmUgZnJlcXVlbmN5IG9mIG51bWJlciBvZiBhZmZlY3RlZCB1c2VycyBwZXIgYXR0YWNrIGluIGVhY2ggY291bnRyeS5JbiAyMDE1LCB0aGUgY3liZXIgYXR0YWNrcyB3aXRoaW4gdGhlIFVTQSBtb3JlIGZyZXF1ZW50bHkgYWZmZWN0ZWQgYWJvdXQgMzEwLDAwMCB1c2VycywgY29tcGFyZWQgdG8gUnVzc2lhIHdoZXJlIGF0dGFja3MgbW9yZSBmcmVxdWVudGx5IGF0dGFja2VkIDY3MCwwMDAgdXNlcnMgYW5kIEdlcm1hbnkgd2hlcmUgbW9zdCBhdHRhY2tzIG1vcmUgZnJlcXVlbnRseSBhZmZlY3RlZCA3OTAsMDAwIHVzZXJzLiBJbiAyMDI0LCB0aGUgbnVtYmVyIG9mIGFmZmVjdGVkIHVzZXJzIGluIHRoZSBVU0EgZG9lcyBub3Qgc2VlbSB0byBiZSBtdWNoIG1vcmUgZGVuc2UgaW4gYW55IG9uZSBudW1iZXIgYW5kIGlzIG1vcmUgZGlzcGVyc2VkLiBKYXBhbiBoYWQgbW9yZSBmcmVxdWVudCBhdHRhY2tzIHRoYXQgYWZmZWN0ZWQgNTAwLDAwMCBhbmQgQnJhemlsIGhhZCBpdCdzIG1vc3QgZnJlcXVlbnQgYXQgNzUwLDAwMCB1c2Vycy4KCmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJywgZmlnLndpZHRoPTcsIGZpZy5oZWlnaHQ9NSwgZmlnLmNhcD0nVGhpcyBwbG90IHNob3dzIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBzZWN1cml0eSB2dWxuZXJhYmlsaXR5IHR5cGUgYW5kIHRoZSBpbnRlcmFjdGlvbiBiZXR3ZWVuIHRoZSB5ZWFyIG9mIGF0dGFjayBhbmQgcmVzb2x1dGlvbiB0aW1lLiBUaGUgcGxvdCBzaG93cyBob3cgb3ZlciB0aGUgeWVhcnMgcmVzb2x1dGlvbiB0aW1lIGhhcyBpbXByb3ZlZCBmb3IgY2VydGFpbiB2dWxuZXJhYmlsaXR5IHR5cGVzLCBzdWNoIGFzIHdlYWsgcGFzc3dvcmRzLCB1bnBhdGNoZWQgc29mdHdhcmUsIGFuZCBzb2NpYWwgZW5naW5lZXJpbmcuIEhvd2V2ZXIsIGZvciB6ZXJvLWRheSB0aGVyZSBpcyBhbiBpbmNyZWFzZSBpbiByZXNvbHV0aW9uIHRpbWUsIHNob3dpbmcgYSBwb3NpdGl2ZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB6ZXJvLWRheSBhbmQgcmVzb2x1dGlvbiB0aW1lLiAnfQoKZ2dwbG90KGN5YmVyLCBhZXMoeCA9IGN5YmVyJFllYXIsIHkgPSBjeWJlciRJbmNpZGVudC5SZXNvbHV0aW9uLlRpbWUuLmluLkhvdXJzLiwgY29sb3IgPSBjeWJlciRTZWN1cml0eS5WdWxuZXJhYmlsaXR5LlR5cGUpKSArCmdlb21fc21vb3RoKGFlcyh4ID0gY3liZXIkWWVhciwgeSA9IGN5YmVyJEluY2lkZW50LlJlc29sdXRpb24uVGltZS4uaW4uSG91cnMuLCBjb2xvciA9IGN5YmVyJFNlY3VyaXR5LlZ1bG5lcmFiaWxpdHkuVHlwZSksIG1ldGhvZCA9ICJsbSIsIHNlID0gRkFMU0UpICsKICBzY2FsZV9zaXplKHJhbmdlID0gYygyLCAxMCkpICsKICBsYWJzKAogICAgdGl0bGUgPSAiUGxvdCBvZiBTZWN1cml0eSBWdWxuZXJhYmlsaXR5LCBZZWFyIG9mIEF0dGFjaywgYW5kIFJlc29sdXRpb24gVGltZSIsCiAgICB4ID0gIlllYXIgb2YgQXR0YWNrIiwKICAgIHkgPSAiSW5jaWRlbnQgUmVzb2x1dGlvbiBUaW1lIGluIEhvdXJzIiwKICAgIGNvbG9yID0gIlNlY3VyaXR5IFZ1bGVybmFiaWxpdHkiCiAgKSArCiAgdGhlbWVfbWluaW1hbCgpCgpgYGAKCmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJywgZmlnLndpZHRoPTcsIGZpZy5oZWlnaHQ9NSwgZmlnLmNhcD0nVGhpcyBwbG90IGZvY3VzZXMgb24gemVyby1kYXkgdnVsbmVyYWJpbGl0eSBhbmQgc2hvd3MgdGhlIHByb3BvcnRpb24gb2YgdGhyZWF0IHR5cGVzIGluIGVhY2ggaW5kdXN0cnkuIFRoZSB4LWF4aXMgc2hvd3MgZWFjaCB0aHJlYXQgdHlwZSBhbmQgdGhlIHktYXhpcyBzaG93cyB0aGUgcHJvcG9ydGlvbiBvZiBhdHRhY2tzIHdpdGhpbiB0aGUgZGF0YXNldCB0aGF0IGZhbGwgaW50byBlYWNoIGNhdGVnb3J5LiBUaGUgZGlmZmVyZW50IGNvbG9ycyBhcmUgbWVhbnQgdG8gc2hvdyBhIHN1YmNhdGVnb3J5LCB0YXJnZXQgaW5kdXN0cnkuIFRoaXMgc2hvd3MgdGhlIHByb3BvcnRpb24gb2YgdGhlIGRhdGFzZXQgdGhhdCBmYWxscyB3aXRoaW4gZWFjaCBjYXRlZ29yeSBvZiB0aHJlYXQgdHlwZSBpbiBlYWNoIHRhcmdldCBpbmR1c3RyeS4nfQojcGxvdCBvZiB6ZXJvLWRheSBhdHRhY2sgdHlwZSBieSB0YXJnZXQgaW5kdXN0cnkKemVyb19kYXRhIDwtIGN5YmVyICU+JQogIGZpbHRlcihTZWN1cml0eS5WdWxuZXJhYmlsaXR5LlR5cGUgPT0gIlplcm8tZGF5IikgCmEgPC0gZ2dwbG90KHplcm9fZGF0YSwgYWVzKHggPSB6ZXJvX2RhdGEkQXR0YWNrLlR5cGUsIGZpbGwgPXplcm9fZGF0YSRUYXJnZXQuSW5kdXN0cnkgKSkgKwogIGdlb21fYmFyKHBvc2l0aW9uID0gImRvZGdlIikgKwogIGxhYnMoCiAgICB4ID0gIlRocmVhdCBUeXBlIiwKICAgIHkgPSAiUHJvcG9ydGlvbiIsCiAgICBmaWxsID0gIlRhcmdldCBJbmR1c3RyeSIsCiAgICB0aXRsZSA9ICJaZXJvLWRheSBJbmNpZGVuY2VzIGJ5IFRocmVhdCBUeXBlIHZzLiBUYXJnZXQgSW5kdXN0cnkiCiAgKSArCiAgdGhlbWVfbWluaW1hbCgpCmdncGxvdGx5KGEpCmBgYAoKWmVyby1kYXkgYXR0YWNrcyBzZWVtIHRvIGNvbW1vbmx5IHVzZSBQaGlzaW5nIHdpdGhpbiBib3RoIHJldGFpbCBhbmQgSVQgaW5kdXN0cmllcywgYmFzZWQgb24gdGhlIGhlaWdodCBhbmQgY291bnQgb2YgdGhlIGJhciBpbiB0aGVzZSB0d28gY2F0ZWdvcmllcy4gUmFuc29td2FyZSBpcyB0aGUgbW9zdCBtaW5pbWFsbHkgdXNlZCB6ZXJvLWRheSBhdHRhY2sgb24gdGhlIGdvdmVybm1lbnQsIHdpdGggYSBjb3VudCBvZiBqdXN0IDEyLgoKYGBge3IgZmlnLmFsaWduPSdjZW50ZXInLCBmaWcud2lkdGg9NywgZmlnLmhlaWdodD01LCBmaWcuY2FwPSdUaGUgYmxveHBsb3Qgc2hvd3MgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGRlZmVuc2UgbWVjaGFuaXNtIGFuZCByZXNvbHV0aW9uIHRpbWUgaW4gaG91cnMuJ30KCmNvbDA9YygiIzQ2ODJCNCIsICIjQjQ0NjRCIiwiIzk5MDBGRiIsIiNGRjY2RkYiLCAiIzAwOTkzMyIpCmJveHBsb3QoSW5jaWRlbnQuUmVzb2x1dGlvbi5UaW1lLi5pbi5Ib3Vycy4gfiBEZWZlbnNlLk1lY2hhbmlzbS5Vc2VkLAogICAgICAgIGRhdGE9Y3liZXIsCiAgICAgICAgeGxhYj0iRGVmZW5zZSBNZWNoYW5pc20iLAogICAgICAgIHlsYWI9IlJlc29sdXRpb24gVGltZSAoaG91cnMpIiwKICAgICAgICBjb2wgPSBjb2wwLAogICAgICAgIG1haW49IkluY2lkZW50IFJlc29sdXRpb24gVGltZSBieSBEZWZlbnNlIE1lY2hhbmlzbSIsCiAgICAgICAgY2V4Lm1haW4gPSAwLjksCiAgICAgICAgY29sLm1haW4gPSAiYmx1ZSIpCgoKYGBgCgpUaGUgYm94cGxvdCBzaG93cyB0aGF0IHRoZSBhdmVyYWdlIHJlc29sdXRpb24gdGltZSBhbW9uZyBhdHRhY2tzIGJhc2VkIG9uIGRlZmVuc2UgbWVjaGFuaXNtcyBpcyByZWxhdGl2ZWx5IHRoZSBzYW1lLiBUaGUgb25seSBleGNlcHRpb24gc2VlbXMgdG8gYmUgZmlyZXdhbGwsIHdoaWNoIGFwcGVhcnMgdG8gdGFrZSBzbGlnaHRseSBsZXNzIHJlc29sdXRpb24gdGltZSBvbiBhdmVyYWdlLldlIGNhbiBpbmRpY2F0ZSB0aGUgYXZlcmFnZSBieSBsb29raW5nIHRoZSBibGFjayBob3Jpem9udGFsIGxpbmUgd2l0aGluIGVhY2ggYm94LiBUaGV5IGFsbCBhcHBlYXIgdG8gbGluZSB1cCB3aXRoIG9uZSBhbm90aGVyIGluZGljYXRpbmcgc2ltaWxhciBhdmVyYWdlcy4KCiMgU2tld25lc3MKCmBgYHtyfQoKY3liZXIkQ291bnRyeVtjeWJlciRDb3VudHJ5PT0iVVNBIl08LTEKY3liZXIkQ291bnRyeTwtIGlmZWxzZShjeWJlciRDb3VudHJ5IT0iVVNBIiwwKQpjeWJlciRTZWN1cml0eS5WdWxuZXJhYmlsaXR5LlR5cGVbY3liZXIkU2VjdXJpdHkuVnVsbmVyYWJpbGl0eS5UeXBlPT0iWmVyby1kYXkiXTwtMQpjeWJlciRTZWN1cml0eS5WdWxuZXJhYmlsaXR5LlR5cGU8LWlmZWxzZShjeWJlciRTZWN1cml0eS5WdWxuZXJhYmlsaXR5LlR5cGUhPSJaZXJvLWRheSIsMCkKY3liZXIkTnVtYmVyLm9mLkFmZmVjdGVkLlVzZXJzPC1hcy5udW1lcmljKGN5YmVyJE51bWJlci5vZi5BZmZlY3RlZC5Vc2VycykKY3liZXIkWWVhcjwtYXMubnVtZXJpYyhhcy5mYWN0b3IoY3liZXIkWWVhcikpCmN5YmVyJEluY2lkZW50LlJlc29sdXRpb24uVGltZS4uaW4uSG91cnMuPC1hcy5udW1lcmljKGN5YmVyJEluY2lkZW50LlJlc29sdXRpb24uVGltZS4uaW4uSG91cnMuKQoKCmM8LXNrZXduZXNzKGN5YmVyJEZpbmFuY2lhbC5Mb3NzLi5pbi5NaWxsaW9uLi4uKQpkPC1za2V3bmVzcyhjeWJlciROdW1iZXIub2YuQWZmZWN0ZWQuVXNlcnMpCmU8LXNrZXduZXNzKGN5YmVyJFllYXIpIAoKU2tld25lc3MgPSBjYmluZChGaW5hbmNpYWwuTG9zcyA9IGMsIAogICAgICAgICAgICAgICAgIEFmZmVjdGVkLlVzZXJzID0gIGQsIAogICAgICAgICAgICAgICAgIFllYXIgPSBlKQoKcGFuZGVyKFNrZXduZXNzKQpgYGAKClRoZSBkYXRhIGlzIG5vcm1hbGx5IHNrZXdlZCBhbmQgbm8gdHJhbnNmb3JtYXRpb24gaXMgbmVjZXNzYXJ5LgoKIyBMaW5lYXIgUmVncmVzc2lvbgoKTGluZWFyIHJlZ3Jlc3Npb24gaXMgZXhwbG9yZWQgYmVsb3c6CgpgYGB7ciBmaWcuYWxpZ249J2NlbnRlcicsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD0xMiwgZmlnLmNhcD0nVGhlIHgtYXhpcyBvZiB0aGUgY29lZmZpY2llbnQgcGF0aCBhbmFseXNpcyBzaG93cyB0aGUgbG9nIG9mIGxhbWJkYS4gQSBsb3cgbGFtYmRhIGhhcyBsZXNzIHJlZ3VsYXJpemF0aW9uIGFuZCBpcyBjbG9zZXIgdG8gb3JkaW5hcnkgbGVhc3Qgc3F1YXJlcy4gQSBoaWdoIGxhbWJkYSBpbmRpY2F0ZXMgaGlnaGVyIHJlZ3VsYXJpemF0aW9uIGFuZCBhdCB0aGlzIGxhbWJkYSBtYW55IGNvZWZmaWNpZW50cyB3aWxsIHNocmluayB0byB6ZXJvLiBUaGUgWS1heGlzIHNob3dzIHRoZSBjb2VmZmljaWVudCB2YWx1ZXMgZm9yIGVhY2ggdmFyaWFibGUgcHJlZGljdG9yIGZvciBmaW5hbmNpYWwgbG9zcy4gVGhlIGxpbmVzIHNob3dzIHRoZSBwYXRoIG9mIGVhY2ggY29lZmZpY2llbnQgaGFzIGxhbWJkYSBkaWZmZXJzLiBUaGUgZGFzaGVkIGxpbmUgc2hvd3MgdGhlIG9wdGltYWwgbGV2ZWwgb2Ygc2hyaW5rYWdlLiBDb2VmZmljaWVudHMgdG8gdGhlIHJpZ2h0IG9mIHRoaXMgbGluZSBhcmUgbW9yZSBmb3JjZWQgdG93YXJkcyB6ZXJvLiBBdCB0aGlzIGNob3NlbiBsYW1iZGEgb25seSBjZXJ0YWluIHZhcmlhYmxlcyB3aWxsIGJlIG5vbi16ZXJvIGFuZCBhcmUgZGVlbWVkIHNpZ25pZmljYW50LiAnfQoKCgoKIyBQcmVkaWN0b3JzICh4KSBhbmQgUmVzcG9uc2UgVmFyaWFibGUgKHkpCgpzZXQuc2VlZCgxMjM0NSkKWCA8LSBhcy5kYXRhLmZyYW1lKGN5YmVyWywgLTVdKSAgCgp5IDwtIGN5YmVyJEZpbmFuY2lhbC5Mb3NzLi5pbi5NaWxsaW9uLi4uCgoKdHJhaW5faW5kZXggPC0gY3JlYXRlRGF0YVBhcnRpdGlvbih5LCBwID0gMC44LCBsaXN0ID0gRkFMU0UpClhfdHJhaW4gPC0gbWFrZVgoWFt0cmFpbl9pbmRleCwgXSkKWF90ZXN0IDwtIG1ha2VYKFhbLXRyYWluX2luZGV4LCBdKQp5X3RyYWluIDwtIHlbdHJhaW5faW5kZXhdCnlfdGVzdCA8LSB5Wy10cmFpbl9pbmRleF0KCiNzdGFuZGFyZGl6aW5nIHRoZSBkYXRhCnByZXByb2Nlc3NfcGFyYW1zIDwtIHByZVByb2Nlc3MoWF90cmFpbiwgbWV0aG9kID0gYygiY2VudGVyIiwic2NhbGUiKSkKWF90cmFpbiA8LSBwcmVkaWN0KHByZXByb2Nlc3NfcGFyYW1zLCBYX3RyYWluKQpYX3Rlc3QgPC0gcHJlZGljdChwcmVwcm9jZXNzX3BhcmFtcywgWF90ZXN0KQoKCiNmaXR0aW5nIHRoZSBtb2RlbApmaXRfbGFzc28gPC0gZ2xtbmV0KFhfdHJhaW4sIHlfdHJhaW4sIGFscGhhID0gMSkgICMgTGFzc28KZml0X3JpZGdlIDwtIGdsbW5ldChYX3RyYWluLCB5X3RyYWluLCBhbHBoYSA9IDApICAjIFJpZGdlCmZpdF9lbGFzdGljX25ldCA8LSBnbG1uZXQoWF90cmFpbiwgeV90cmFpbiwgYWxwaGEgPSAwLjUpICAjIEVsYXN0aWMgTmV0CiNjcm9zcy12YWxpZGF0aW9uCmN2X2xhc3NvIDwtIGN2LmdsbW5ldChYX3RyYWluLCB5X3RyYWluLCBhbHBoYSA9IDEpICAgICAgICMgTGFzc28gQ1YKY3ZfcmlkZ2UgPC0gY3YuZ2xtbmV0KFhfdHJhaW4sIHlfdHJhaW4sIGFscGhhID0gMCkgICAgICAgIyBSaWRnZSBDVgpjdl9lbGFzdGljX25ldCA8LSBjdi5nbG1uZXQoWF90cmFpbiwgeV90cmFpbiwgYWxwaGEgPSAwLjUpICAjIEVsYXN0aWMgTmV0IENWCgojcGxvdCBjb2VmZmljaWVudCBwYXRoCnBhcihtYXI9Yyg1LDQsNiwzKSkKIyBQbG90IGNvZWZmaWNpZW50IHBhdGgKcGxvdChmaXRfbGFzc28sIHh2YXIgPSAibGFtYmRhIiwgbGFiZWwgPSBUUlVFLAogICAgIGx3ZCA9IDEuNSwKICAgICBtYWluID0gIkNvZWZmaWNpZW50IFBhdGggQW5hbHlzaXM6IExBU1NPIiwKICAgICBjZXgubWFpbiA9IDAuOSwKICAgICBjb2wgPSByYWluYm93KDEwKSkKYWJsaW5lKHYgPSBsb2coMSksIGNvbCA9ICJwdXJwbGUiLCBsdHkgPSA0LCBsd2QgPSAyKQphYmxpbmUodiA9IC0xLCBjb2wgPSAic3RlZWxibHVlIiwgbHR5ID0gMiwgbHdkID0gMikKCmBgYAoKYGBge3J9CgpwYW5kZXIoZGF0YS5mcmFtZShjb2xuYW1lcyhYX3RyYWluKVtjKDI3LCA1LCAyMCwgMTIpXSksY2FwdGlvbj0gIkZlYXR1cmUgVmFyaWFibGVzIGF0IGxvZyhMYW1iZGEpID0gMCIpCgpwYW5kZXIoZGF0YS5mcmFtZShjb2xuYW1lcyhYX3RyYWluKVtjKDEyLCAyMCwgNSwgMjYsIDE0LCAxLCAzNSwgOSwgMzksIDM2LCA0LCAzLCAyMSwgMTksIDIsNildKSxjYXB0aW9uPSAiIEZlYXR1cmUgVmFyaWFibGVzIGF0IGxvZyhMYW1iZGEpID0gLTEiKQpgYGAKCmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJywgZmlnLndpZHRoPTcsIGZpZy5oZWlnaHQ9NSwgZmlnLmNhcD0nVGhlIHBsb3Qgb2YgcGVyZm9ybWFuY2Ugc2hvd3MgdGhhdCBhcyBsYW1iZGEgaW5jcmVhc2VzLCB0aGUgTVNFIGRlY3JlYXNlcy4gVGhlIHR3byB2ZXJ0aWNhbCBsaW5lcyBnaXZlIGEgcmVmZXJlbmNlIG9mIGxhbWJkYS4gJ30KcGFyKG1hcj1jKDUsNCw2LDMpKQojIwpwbG90KGN2X2xhc3NvLCBtYWluID0gIlJNU0UgUGxvdDogTEFTU08iLAogICAgIGNleC5tYWluID0gMC45KQpgYGAKCmBgYHtyfQojIEV4dHJhY3QgY29lZmZpY2llbnRzIGZvciB0aGUgYmVzdCBsYW1iZGEKYmVzdC5sYXNzby5sYW1iZGEgPC0gY3ZfbGFzc28kbGFtYmRhLm1pbgpiZXN0LnJpZGdlLmxhbWJkYSA8LSBjdl9yaWRnZSRsYW1iZGEubWluCmJlc3QuZWxhc3RpYy5uZXQubGFtYmRhIDwtIGN2X2VsYXN0aWNfbmV0JGxhbWJkYS5taW4KIyMKIyBMYXNzbyBSZWdyZXNzaW9uIChMMSBSZWd1bGFyaXphdGlvbik6IAojIENBVVRJT046IG1vZGVsIGZvcm11bGEgZGlmZmVycyBmcm9tIHRoZSByZWd1bGFyIHJlZ3Jlc3Npb24gZm9ybXVsYSAKbGFzc29fbW9kZWwub3B0IDwtIGdsbW5ldChYX3RyYWluLCAKICAgICAgICAgICAgICAgICAgICAgIHlfdHJhaW4sIAogICAgICAgICAgICAgICAgICAgICAgYWxwaGEgPSAxLCAgICAgICMgbGFzc28gcmVncmVzc2lvbiAKICAgICAgICAgICAgICAgICAgICAgIGxhbWJkYSA9IGJlc3QubGFzc28ubGFtYmRhLHN0YW5kYXJkaXplID0gVFJVRSkgICAjIHVzZXNlciBzZWxlY3RlZCBhbHBoYSwgb3B0aW1hbCBsYW1iZGEKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBjYW4gYmUgb2J0YWluZWQgdGhyb3VnaCBDViAoc2VlIGJlbG93KQpsYXNzb19wcmVkaWN0aW9ucy5vcHQgPC0gcHJlZGljdChsYXNzb19tb2RlbC5vcHQsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHMgPSBiZXN0Lmxhc3NvLmxhbWJkYSwgIyB1c2VyIHNlbGVjdGVkIGxhbWJkYSB2YWx1ZSAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIChyZWd1bGFyaXphdGlvbiBwYXJlbWV0ZXIpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmV3eCA9IFhfdGVzdCkgICMgdGVzdCBkYXRhIHNldCAKIyBUaGUgZm9sbG93aW5nIFJNU0Ugb2YgcHJlZGljdGlvbiBzZXJ2ZXMgYXMgYSB2YWxpZGF0aW9uIC0gb25lIHN0ZXAgdmFsaWRhdGlvbgpsYXNzb19ybXNlLm9wdCA8LSBzcXJ0KG1lYW4oKHlfdGVzdCAtIGxhc3NvX3ByZWRpY3Rpb25zLm9wdCleMikpICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAojIFJpZGdlIFJlZ3Jlc3Npb24gKEwyIFJlZ3VsYXJpemF0aW9uKQpyaWRnZV9tb2RlbC5vcHQgPC0gZ2xtbmV0KFhfdHJhaW4sIHlfdHJhaW4sIGFscGhhID0gMCwgbGFtYmRhID0gYmVzdC5yaWRnZS5sYW1iZGEsc3RhbmRhcmRpemU9VFJVRSkKcmlkZ2VfcHJlZGljdGlvbnMub3B0IDwtIHByZWRpY3QocmlkZ2VfbW9kZWwub3B0LCBzID0gYmVzdC5yaWRnZS5sYW1iZGEsIG5ld3ggPSBYX3Rlc3QpCnJpZGdlX3Jtc2Uub3B0IDwtIHNxcnQobWVhbigoeV90ZXN0IC0gcmlkZ2VfcHJlZGljdGlvbnMub3B0KV4yKSkKCiMgRWxhc3RpYyBOZXQgKENvbWJpbmF0aW9uIG9mIEwxIGFuZCBMMikKZWxhc3RpY19uZXRfbW9kZWwub3B0IDwtIGdsbW5ldChYX3RyYWluLCB5X3RyYWluLCBhbHBoYSA9IDAuNSwgbGFtYmRhID0gMC4xLHN0YW5kYXJkaXplID0gVFJVRSkKZWxhc3RpY19uZXRfcHJlZGljdGlvbnMub3B0IDwtIHByZWRpY3QoZWxhc3RpY19uZXRfbW9kZWwub3B0LCBzID0gMC4xLCBuZXd4ID0gWF90ZXN0KQplbGFzdGljX25ldF9ybXNlLm9wdCA8LSBzcXJ0KG1lYW4oKHlfdGVzdCAtIGVsYXN0aWNfbmV0X3ByZWRpY3Rpb25zLm9wdCleMikpCgpSTVNFLm9wdCA9IGNiaW5kKExBU1NPLm9wdCA9IGxhc3NvX3Jtc2Uub3B0LCAKICAgICAgICAgICAgICAgICBSaWRnZS5vcHQgPSAgcmlkZ2Vfcm1zZS5vcHQsIAogICAgICAgICAgICAgICAgIEVsYXN0aWNuZXQub3B0ID0gZWxhc3RpY19uZXRfcm1zZS5vcHQpCgpwYW5kZXIoUk1TRS5vcHQpCmBgYAoKVGhlIG9wdGltYWwgbGFtYmRhIGZvciBlYWNoIHR5cGUgb2YgbGluZWFyIHJlZ3Jlc3Npb24gaXMgc2hvd24gYWJvdmUuCgojIyBMQVNTTyBSZWdyZXNzaW9uIEVxdWF0aW9uCgpOb3csIGEgZmluYWwgbW9kZWwgd2lsbCBiZSBleHRyYWN0ZWQgdXNpbmcgTEFTU08sIFJpZGdlLCBhbmQgRWxhc3RpYyBOZXQuCgpgYGB7cn0KI0xhc3NvCiMgRXh0cmFjdCBjb2VmZmljaWVudHMgd2l0aCBuYW1lcyBmcm9tIGdsbW5ldApjb2VmX2xhc3NvIDwtIGNvZWYoY3ZfbGFzc28sIHMgPSBiZXN0Lmxhc3NvLmxhbWJkYSkKY29lZl9kZiA8LSBhcy5kYXRhLmZyYW1lKGFzLm1hdHJpeChjb2VmX2xhc3NvKSkKY29lZl9kZiRmZWF0dXJlIDwtIHJvd25hbWVzKGNvZWZfZGYpCmNvbG5hbWVzKGNvZWZfZGYpWzFdIDwtICJjb2VmZmljaWVudCIKCiMgRmlsdGVyIG5vbi16ZXJvIGNvZWZmaWNpZW50cwpub256ZXJvX2NvZWZzIDwtIHN1YnNldChjb2VmX2RmLCBjb2VmZmljaWVudCAhPSAwKQoKIyBTZXBhcmF0ZSBpbnRlcmNlcHQKaW50ZXJjZXB0IDwtIHJvdW5kKG5vbnplcm9fY29lZnMkY29lZmZpY2llbnRbbm9uemVyb19jb2VmcyRmZWF0dXJlID09ICIoSW50ZXJjZXB0KSJdLCA0KQpub256ZXJvX3Rlcm1zIDwtIHN1YnNldChub256ZXJvX2NvZWZzLCBmZWF0dXJlICE9ICIoSW50ZXJjZXB0KSIpCgojIEJ1aWxkIG1vZGVsIGVxdWF0aW9uIHN0cmluZwplcXVhdGlvbiA8LSBwYXN0ZTAocm91bmQobm9uemVyb190ZXJtcyRjb2VmZmljaWVudCwgNCksICIqIiwgbm9uemVyb190ZXJtcyRmZWF0dXJlKQpjYXQoIk1vZGVsIGVxdWF0aW9uOiB5ID0iLCBpbnRlcmNlcHQsICIrIiwgcGFzdGUoZXF1YXRpb24sIGNvbGxhcHNlID0gIiArICIpLCAiXG4iKQoKCmBgYAoKIyMgUmlkZ2UgUmVncmVzc2lvbiBFcXVhdGlvbgoKYGBge3J9CgojIEV4dHJhY3QgY29lZmZpY2llbnRzIHdpdGggbmFtZXMgZnJvbSBnbG1uZXQKY29lZl9yaWRnZSA8LSBjb2VmKGN2X3JpZGdlLCBzID0gYmVzdC5yaWRnZS5sYW1iZGEpCmNvZWZfZGYgPC0gYXMuZGF0YS5mcmFtZShhcy5tYXRyaXgoY29lZl9yaWRnZSkpCmNvZWZfZGYkZmVhdHVyZSA8LSByb3duYW1lcyhjb2VmX2RmKQpjb2xuYW1lcyhjb2VmX2RmKVsxXSA8LSAiY29lZmZpY2llbnQiCgojIEZpbHRlciBub24temVybyBjb2VmZmljaWVudHMKbm9uemVyb19jb2VmcyA8LSBzdWJzZXQoY29lZl9kZiwgY29lZmZpY2llbnQgIT0gMCkKCiMgU2VwYXJhdGUgaW50ZXJjZXB0CmludGVyY2VwdCA8LSByb3VuZChub256ZXJvX2NvZWZzJGNvZWZmaWNpZW50W25vbnplcm9fY29lZnMkZmVhdHVyZSA9PSAiKEludGVyY2VwdCkiXSwgNCkKbm9uemVyb190ZXJtcyA8LSBzdWJzZXQobm9uemVyb19jb2VmcywgZmVhdHVyZSAhPSAiKEludGVyY2VwdCkiKQoKIyBCdWlsZCBtb2RlbCBlcXVhdGlvbiBzdHJpbmcKZXF1YXRpb24gPC0gcGFzdGUwKHJvdW5kKG5vbnplcm9fdGVybXMkY29lZmZpY2llbnQsIDQpLCAiKiIsIG5vbnplcm9fdGVybXMkZmVhdHVyZSkKY2F0KCJNb2RlbCBlcXVhdGlvbjogeSA9IiwgaW50ZXJjZXB0LCAiKyIsIHBhc3RlKGVxdWF0aW9uLCBjb2xsYXBzZSA9ICIgKyAiKSwgIlxuIikKCgoKYGBgCgojIyBFbGFzdGljIE5ldCBSZWdyZXNzaW9uIEVxdWF0aW9uCgpgYGB7cn0KCiMgRXh0cmFjdCBjb2VmZmljaWVudHMgd2l0aCBuYW1lcyBmcm9tIGdsbW5ldApjb2VmX2VsYXN0aWMgPC0gY29lZihjdl9lbGFzdGljX25ldCwgcyA9IGJlc3QuZWxhc3RpYy5uZXQubGFtYmRhKQpjb2VmX2RmIDwtIGFzLmRhdGEuZnJhbWUoYXMubWF0cml4KGNvZWZfZWxhc3RpYykpCmNvZWZfZGYkZmVhdHVyZSA8LSByb3duYW1lcyhjb2VmX2RmKQpjb2xuYW1lcyhjb2VmX2RmKVsxXSA8LSAiY29lZmZpY2llbnQiCgojIEZpbHRlciBub24temVybyBjb2VmZmljaWVudHMKbm9uemVyb19jb2VmcyA8LSBzdWJzZXQoY29lZl9kZiwgY29lZmZpY2llbnQgIT0gMCkKCiMgU2VwYXJhdGUgaW50ZXJjZXB0CmludGVyY2VwdCA8LSByb3VuZChub256ZXJvX2NvZWZzJGNvZWZmaWNpZW50W25vbnplcm9fY29lZnMkZmVhdHVyZSA9PSAiKEludGVyY2VwdCkiXSwgNCkKbm9uemVyb190ZXJtcyA8LSBzdWJzZXQobm9uemVyb19jb2VmcywgZmVhdHVyZSAhPSAiKEludGVyY2VwdCkiKQoKIyBCdWlsZCBtb2RlbCBlcXVhdGlvbiBzdHJpbmcKZXF1YXRpb24gPC0gcGFzdGUwKHJvdW5kKG5vbnplcm9fdGVybXMkY29lZmZpY2llbnQsIDQpLCAiKiIsIG5vbnplcm9fdGVybXMkZmVhdHVyZSkKY2F0KCJNb2RlbCBlcXVhdGlvbjogeSA9IiwgaW50ZXJjZXB0LCAiKyIsIHBhc3RlKGVxdWF0aW9uLCBjb2xsYXBzZSA9ICIgKyAiKSwgIlxuIikKCgoKYGBgCgojIENvbXBhcmluZyBMaW5lYXIgUmVncmVzc2lvbiB3aXRoIFNWUgoKYGBge3J9CgpjeWJlciA8LSByZWFkLmNzdignaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL0dhYmJ5SzgvRGF0YXNldHMvcmVmcy9oZWFkcy9tYWluL0dsb2JhbF9DeWJlcnNlY3VyaXR5X1RocmVhdHNfMjAxNS0yMDI0LmNzdicpCgojT25lLUhvdCBFbmNvZGluZwpkdW1teSA8LSBkdW1teVZhcnMoIn4uIiwgZGF0YT1jeWJlcikKY3liZXIyPC1kYXRhLmZyYW1lKHByZWRpY3QoZHVtbXksIG5ld2RhdGE9Y3liZXIpKQoKIyMjIyMKIyBTcGxpdCBkYXRhIGludG8gZmVhdHVyZXMgKFgpIGFuZCB0YXJnZXQgKHkpClggPC0gY3liZXIyWywgLTI1XSAgIyBBbGwgY29sdW1ucyBleGNlcHQgdGhlIHRhcmdldCB2YXJpYWJsZQp5IDwtIGN5YmVyMlssIDI1XSAgICMgVGFyZ2V0IHZhcmlhYmxlIChGaW5hbmNpYWwgTG9zcyBpbiBNaWxsaW9ucykKCiMgY3JlYXRlIGR1bW15IHZhcmlhYmxlcwoKCgojIyMjIwojIFNwbGl0IGRhdGEgaW50byB0cmFpbmluZyBhbmQgdGVzdGluZyBzZXRzCnNldC5zZWVkKDEyMykKdHJhaW4uaW5kZXggPC0gc2FtcGxlKDE6bnJvdyhjeWJlcjIpLCAwLjggKiBucm93KGN5YmVyMikpClgudHJhaW4gPC0gWFt0cmFpbi5pbmRleCwgXQp5LnRyYWluIDwtIHlbdHJhaW4uaW5kZXhdClgudGVzdCA8LSBYWy10cmFpbi5pbmRleCwgXQp5LnRlc3QgPC0geVstdHJhaW4uaW5kZXhdCgojIyMjIwpjeWJlcjIudHJhaW4gPC0gYXMuZGF0YS5mcmFtZShjeWJlcjJbdHJhaW4uaW5kZXgsIF0pCmN5YmVyMi50ZXN0IDwtIGN5YmVyMlstdHJhaW4uaW5kZXgsIF0KIyMjIyMKIyMgU2V0IHVwIGN1c3RvbSBjcm9zcy12YWxpZGF0aW9uIGNvbnRyb2wKbXlfdHVuZV9jb250cm9sIDwtIHR1bmUuY29udHJvbCgKICBjcm9zcyA9IDUsICAjIFVzZSA1LWZvbGQgY3Jvc3MtdmFsaWRhdGlvbiwgdGhlIGRlZmF1bHQgaXMgMTAtZm9sZCBjcm9zcy12YWxpZGF0aW9uCiAgbnJlcGVhdCA9IDEgIyBOdW1iZXIgb2YgcmVwZXRpdGlvbnMgKGZvciByZXBlYXRlZCBjcm9zcy12YWxpZGF0aW9uKQopCiMjIyMjCiMgUGVyZm9ybSBncmlkIHNlYXJjaCBmb3IgaHlwZXJwYXJhbWV0ZXIgdHVuaW5nOiBSQkYga2VybmVsIGlzIHVzZWQKdHVuZS5SQkYgPC0gdHVuZShzdm0sCiAgICAgICAgICAgICAgICAgdHJhaW4ueCA9IFgudHJhaW4sIAogICAgICAgICAgICAgICAgIHRyYWluLnkgPSB5LnRyYWluLCAKICAgICAgICAgICAgICAgICAgICByYW5nZXMgPSBsaXN0KGVwc2lsb24gPSBzZXEoMC4xLCAwLjUsIDAuMSksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29zdCA9IGMoMSwgMTAsIDEwMCksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2FtbWEgPSBjKDAuMDEsIDAuMSwgMSkpLCAjIEh5cGVycGFyIGluIFJCRgogICAgICAgICAgICAgICAgICAgIHR1bmVjb250cm9sID0gbXlfdHVuZV9jb250cm9sICAgICMgNS1mb2xkIGNyb3NzLXZhbGlkYXRpb24KCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKCiMjIyMKIyBEaXNwbGF5IHRoZSBiZXN0IHBhcmFtZXRlcnMKI3ByaW50KHR1bmUucmVzdWx0cyRiZXN0LnBhcmFtZXRlcnMpCiMjIyMjCiMgVHJhaW4gdGhlIGZpbmFsIG1vZGVsIHVzaW5nIHRoZSBiZXN0IHBhcmFtZXRlcnMKZmluYWwuUkJGPC0gc3ZtKFgudHJhaW4sIHkudHJhaW4sIAogICAgICAgICAgICAgICAgICAgdHlwZSA9ICJlcHMtcmVncmVzc2lvbiIsICAjIFVzZSAibnUtcmVncmVzc2lvbiIgZm9yIG51LVNWUgogICAgICAgICAgICAgICAgICAga2VybmVsID0gInJhZGlhbCIsIAogICAgICAgICAgICAgICAgICAgZXBzaWxvbiA9IHR1bmUuUkJGJGJlc3QucGFyYW1ldGVycyRlcHNpbG9uLCAKICAgICAgICAgICAgICAgICAgIGNvc3QgPSB0dW5lLlJCRiRiZXN0LnBhcmFtZXRlcnMkY29zdCwgCiAgICAgICAgICAgICAgICAgICBnYW1tYSA9IHR1bmUuUkJGJGJlc3QucGFyYW1ldGVycyRnYW1tYSkKIyMjIyMKIyBNYWtlIHByZWRpY3Rpb25zIG9uIHRoZSB0ZXN0IHNldApwcmVkLlJCRiA8LSBwcmVkaWN0KGZpbmFsLlJCRiwgWC50ZXN0KQoKIyBFdmFsdWF0ZSBwZXJmb3JtYW5jZQptc2UuUkJGIDwtIG1lYW4oKHkudGVzdCAtIHByZWQuUkJGKV4yKSAgICAjIG1lYW4gc3F1YXJlIGVycm9yCm1hZS5SQkYgPC0gbWVhbihhYnMoeS50ZXN0IC0gcHJlZC5SQkYpKSAgICMgbWVhbiBhYnNvbHV0ZSBlcnJvcgoKIyMjIyBsaW5lYXIga2VybmVsCgojIFBlcmZvcm0gZ3JpZCBzZWFyY2ggZm9yIGh5cGVycGFyYW1ldGVyIHR1bmluZzogUkJGIGtlcm5lbCBpcyB1c2VkCnR1bmUubGluIDwtIHR1bmUoc3ZtLCB0cmFpbi54ID0gWC50cmFpbiwgdHJhaW4ueSA9IHkudHJhaW4sIAogICAgICAgICAgICAgICAgICAgIHJhbmdlcyA9IGxpc3QoZXBzaWxvbiA9IHNlcSgwLjEsIDAuNSwgMC4xKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb3N0ID0gYygxLCAxMCwgMTAwKSksIAogICAgICAgICAgICAgICAgICAgIHR1bmVjb250cm9sID0gbXlfdHVuZV9jb250cm9sICMgNS1mb2xkIGNyb3NzLXZhbGlkYXRpb24KICAgICAgICAgICAgICAgICAgICApCiMjIyMKIyBEaXNwbGF5IHRoZSBiZXN0IHBhcmFtZXRlcnMKI3ByaW50KHR1bmUubGluJGJlc3QucGFyYW1ldGVycykKIyMjIyMKIyBUcmFpbiB0aGUgZmluYWwgbW9kZWwgdXNpbmcgdGhlIGJlc3QgcGFyYW1ldGVycwpmaW5hbC5saW48LSBzdm0oWC50cmFpbiwgeS50cmFpbiwgCiAgICAgICAgICAgICAgICAgICB0eXBlID0gImVwcy1yZWdyZXNzaW9uIiwgICMgVXNlICJudS1yZWdyZXNzaW9uIiBmb3IgbnUtU1ZSCiAgICAgICAgICAgICAgICAgICBrZXJuZWwgPSAibGluZWFyIiwgCiAgICAgICAgICAgICAgICAgICBlcHNpbG9uID0gdHVuZS5saW4kYmVzdC5wYXJhbWV0ZXJzJGVwc2lsb24sIAogICAgICAgICAgICAgICAgICAgY29zdCA9IHR1bmUubGluJGJlc3QucGFyYW1ldGVycyRjb3N0KQojIyMjIwojIE1ha2UgcHJlZGljdGlvbnMgb24gdGhlIHRlc3Qgc2V0CnByZWQubGluIDwtIHByZWRpY3QoZmluYWwubGluLCBYLnRlc3QpCgojIEV2YWx1YXRlIHBlcmZvcm1hbmNlCm1zZS5saW4gPC0gbWVhbigoeS50ZXN0IC0gcHJlZC5saW4pXjIpICAgICMgbWVhbiBzcXVhcmUgZXJyb3IKbWFlLmxpbiA8LSBtZWFuKGFicyh5LnRlc3QgLSBwcmVkLmxpbikpCgojIEFmdGVyIHR1bmluZyBpcyBjb21wbGV0ZQoKCgpgYGAKCmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJywgZmlnLndpZHRoPTcsIGZpZy5oZWlnaHQ9NSwgZmlnLmNhcD0nVGhlIHBsb3RzIGFyZSBhIGRpYWdub3N0aWMgcGxvdCBmb3IgdGhlIHJlZ3Jlc3Npb24gbW9kZWwuJ30KCiMjIG9yZGluYXJ5IExTRSByZWdyZXNzaW9uIG1vZGVsIHdpdGggc3RlcHdpc2UgdmFyaWFibGUgc2VsZWN0aW9uCmxzZS5maXQgPC0gbG0oRmluYW5jaWFsLkxvc3MuLmluLk1pbGxpb24uLi5+LixkYXRhPWN5YmVyMi50cmFpbikKQUlDLmZpdCA8LSBzdGVwQUlDKGxzZS5maXQsZGlyZWN0aW9uPSJib3RoIiwgdHJhY2UgPSBGQUxTRSkKcHJlZC5sc2UgPC0gcHJlZGljdChBSUMuZml0LCBYLnRlc3QpCm1zZS5sc2UgPC0gbWVhbigoeS50ZXN0IC0gcHJlZC5sc2UpXjIpICAgICMgbWVhbiBzcXVhcmUgZXJyb3IKbWFlLmxzZSA8LSBtZWFuKGFicyh5LnRlc3QgLSBwcmVkLmxzZSkpICAgIyBtZWFuIGFic29sdXRlIGVycm9yCiMjIwpwYXIobWZyb3c9YygyLDIpLCBtYXI9YygyLDIsMiwyKSkKcGxvdChBSUMuZml0KQoKCmBgYAoKYGBge3J9CiMjIwpQZXJmb3JtYW5jZSA8LSBkYXRhLmZyYW1lKFJCRi5TVlI9Yyhtc2UuUkJGLCBtYWUuUkJGKSwKICAgICAgICAgICAgICAgICAgICAgICAgICBMaW5lYXIuU1ZSID0gYyhtc2UubGluLCBtYWUubGluKSwKICAgICAgICAgICAgICAgICAgICAgICAgICBMU0UuUmVnID1jKG1zZS5sc2UsIG1hZS5sc2UpKQpyb3cubmFtZXMoUGVyZm9ybWFuY2UpIDwtIGMoIk1TRSIsICJNQUUiKQojIwpwYW5kZXIoUGVyZm9ybWFuY2UpCmBgYAoKQmFzZWQgb24gb3VyIE1TRSBhbmQgTUFFIHNjb3Jlcywgb3VyIGxlYXN0IHNxdWFyZXMgZXN0aW1hdGlvbiBoYXMgdGhlIGJlc3QgbW9kZWwuIEl0IGhhcyBsb3dlciBNU0UgYW5kIE1BRSBzY29yZXMgaW5kaWNhdGluZyBhIGJldHRlciBmaXQgbW9kZWwuCg==